using SoundEngine3;
using SoundEngine3.AudioProcessing;
using SoundEngine3.DspPlugins;
using SoundEngine3.DspPlugins.Meta;
using SoundEngine3.MIDI.Meta;
using System;
using System.Collections.Generic;
using ZquenceStudio3.Core;
using ZquenceStudio3.Core.DspPlugins;
using ZquenceStudio3.DspPlugins;

namespace ZquenceStudio.DefaultDspPlugins.Effects
{
    public class StereoSplitterDescription : IDspPlugin
    {
        public DspPluginInfo GetDspPluginInfo()
        {
            return new DspPluginInfo("StereoSplitter", DspType.Effect);
        }

        public BaseDspPluginInstance CreateDsp(ZquenceStudio3Context context, DspPluginInfo info)
        {
            return new StereoSplitter(context, info);
        }
    }

    public class StereoSplitter : CoreDspEffectPlugin
    {
        private double[] m_restSound;
        private ulong m_totalSmaple = 0;
        private int m_recordSample = -1;
        private bool m_startUseRecorded = false;
        private int mSplitStereo;

        /// <summary>
        /// Get or set the split of stereo in samples.
        /// </summary>
        public int SplitStereo
        {
            get
            {
                return mSplitStereo;
            }
            set
            {
                mSplitStereo = value;
                Reset();
            }
        }

        public StereoSplitter(ZquenceStudio3Context application, DspPluginInfo info)
            : base(application, info)
        {
            CreateParameters();

            SplitStereo = 500;

            SupportAudioProcessing = true;
        }

        private void CreateParameters()
        {
            // Volyme.
            ParameterPropertiesDirect split = new ParameterPropertiesDirect(this);
            split.Label = "Split in samples";
            split.DirectGet += delegate() { return SplitStereo; };
            split.DirectSet += delegate(double value) { SplitStereo = (int)value; };
            split.MinValue = 1;
            split.MaxValue = 4000;
            split.IsInteger = true;
            AddParameter(split);
        }
        
        public override Version Version
        {
            get { return DspPluginInfo.DefaultVersion; }
        }

        public override string PluginInfo
        {
            get { return DspPluginInfo.DefaultPluginInfo; }
        }
        
        public void Reset()
        {
            m_totalSmaple = 0;
            m_recordSample = 0;
            m_startUseRecorded = false;

            if (m_restSound == null)
            {
                m_restSound = new double[SplitStereo];
            }
            else if (SplitStereo > m_restSound.Length)
            {
                m_restSound = new double[SplitStereo];
            }
        }

        public override void Process(BaseAudioBuffer input, BaseAudioBuffer output)
        {
            double[] sound = input.Channels[1].Samples;
            double[] sound_output = output.Channels[1].Samples;

            double deltaOffsetInSamples = SoundEngineContext.PPQ_To_Samples(1);

            uint splitStereo = (uint)SplitStereo;

            // Take rest sound from the right of the array.
            int start = 0;
            int stop = SoundEngineContext.BufferSize;
            for (int i = start; i < stop; i++)
            {
                // If totalsample is over the SplitStereo time.
                if (m_totalSmaple++ > splitStereo)
                {
                    // Mod.
                    if (m_recordSample >= splitStereo)
                    {
                        m_recordSample %= (int)splitStereo;
                        m_startUseRecorded = true; // Now. Start use the recorded sound.

                    }
                    // Then start to record audio.
                    m_restSound[m_recordSample++] = sound[i];
                }

                if (m_startUseRecorded)
                {
                    int forwardIndex = m_recordSample + 1;
                    if (forwardIndex >= splitStereo)
                    {
                        forwardIndex %= (int)splitStereo;
                    }

                    sound_output[i] = m_restSound[forwardIndex];
                }
            }
        }
        
        public override void Dispose()
        {

        }

        protected override void OnProcessEvents(List<MidiMessage> midiMessages)
        {

        }

        protected override void OnChangeBufferSize(int bufferSize)
        {
            Reset();
        }

        protected override void OnChangeSampleRate(double sampleRate)
        {
            Reset();
        }

        protected override void OnChangeInputChannels(int channels)
        {

        }

        protected override void OnChangeOutputChannels(int channels)
        {

        }
    }
}
